/************************************************************************/
// Copyright (C) 2016, han_gangbiao. All rights reserved.
// Use of this source code is governed by a GPL-v2 license that can be found in the License file.
// 
// [Time]:      2016-1-4 21:53:17
// [Author]:    han_gangbiao [http://my.oschina.net/jackhen]
// [Info]:      
/************************************************************************/

#include "net_binary_pdu.h"
#include "net_proto_def.h"

NetBinaryPdu::NetBinaryPdu()
{
    memset(&m_header, 0, sizeof(m_header));
    m_body.clear(); m_body.push_back('\0');
    m_stream.clear(); m_stream.push_back('\0');
}

NetBinaryPdu::~NetBinaryPdu()
{
    memset(&m_header, 0, sizeof(m_header));
    m_body.clear();
    m_stream.clear();
}

smart::ref_ptr<NetBinaryPdu> NetBinaryPdu::createPduFromStream(const char* data, int len, int& rPduLen)
{
    NetBinaryPdu* pPDU = new NetBinaryPdu();
    rPduLen = pPDU->fromStream(data, len);
    if(rPduLen < 0)
    {
        SAFE_DELETE(pPDU);
    }

    return pPDU;
}


int NetBinaryPdu::getHeaderLen() const
{
    return sizeof(m_header);
}

uint32 NetBinaryPdu::getPduLength() const
{
    return m_header.length;
}

uint16 NetBinaryPdu::getVersion() const
{
    return m_header.version;
}

uint16 NetBinaryPdu::getUnit() const
{
    return m_header.unit;
}

uint16 NetBinaryPdu::getFlag() const
{
    return m_header.flag;
}

uint16 NetBinaryPdu::getOperation() const
{
    return m_header.operation;
}

uint32 NetBinaryPdu::getSeq() const
{
    return m_header.seq;
}

uint32 NetBinaryPdu::getReserved() const
{
    return m_header.reserved;
}

void NetBinaryPdu::setPduLength( uint32 length )
{
    m_header.length = length;
}

void NetBinaryPdu::setVersion( uint16 version )
{
    m_header.version = version;
}

void NetBinaryPdu::setUnit( uint16 unit )
{
    m_header.unit = unit;
}

void NetBinaryPdu::setFlag( uint16 flag )
{
    m_header.flag = flag;
}

void NetBinaryPdu::setOperation( uint16 operation )
{
    m_header.operation = operation;
}

void NetBinaryPdu::setSeq( uint32 seq )
{
    m_header.seq = seq;
}

void NetBinaryPdu::setReserved( uint32 reserved )
{
    m_header.reserved = reserved;
}

const char* NetBinaryPdu::getBody() const
{
    return &m_body[0];
}

int NetBinaryPdu::getBodyLen() const
{
    return (int)m_body.size() - 1;
}

int NetBinaryPdu::setBody( const char* data, int len /*= -1*/ )
{
    if(len == 0)
        return 0;
    int bodyLen = len;
    if(bodyLen < 0)
    {
        bodyLen = (int)strlen(data);
    }

    m_body.resize(bodyLen + 1);
    memcpy(&m_body[0], data, bodyLen);
    m_body[bodyLen] = '\0';

    return 0;
}

const char* NetBinaryPdu::getBinaryStream() const
{
    return &m_stream[0];
}

int NetBinaryPdu::getBinaryStreamLength() const
{
    return (int)m_stream.size() - 1;
}

const char* NetBinaryPdu::toStream()
{
    int head_len = sizeof(m_header);
    int total_len = head_len + getBodyLen();

    setPduLength(total_len);
    setVersion(NET_PDU_VERSION);

    m_stream.resize(total_len + 1);
    //memcpy(&m_stream[0], &m_header, head_len);
    //header -- bytestream
    char* buf = &m_stream[0];
    writeUInt32(buf, m_header.length);      buf += 4;
    writeUInt16(buf, m_header.version);     buf += 2;
    writeUInt16(buf, m_header.unit);        buf += 2;
    writeUInt16(buf, m_header.flag);        buf += 2;
    writeUInt16(buf, m_header.operation);   buf += 2;
    writeUInt32(buf, m_header.seq);         buf += 4;
    writeUInt32(buf, m_header.reserved);    buf += 4;


    //body
    memcpy(&m_stream[head_len], getBody(), getBodyLen());
    m_stream[total_len] = '\0';

    return &m_stream[0];
}

int NetBinaryPdu::fromStream( const char* data, int len )
{
    int head_len = sizeof(m_header);
    if(len < head_len)
    {
        //printf("from Stream len<head_len\n");
        return -1;
    }

    //memcpy(&m_header, data, head_len);
    //header -- bytestream
    const char* buf = data;
    m_header.length = readUInt32(buf);      buf += 4;
    m_header.version = readUInt16(buf);     buf += 2;
    m_header.unit = readUInt16(buf);        buf += 2;
    m_header.flag = readUInt16(buf);        buf += 2;
    m_header.operation = readUInt16(buf);   buf += 2;
    m_header.seq = readUInt32(buf);         buf += 4;
    m_header.reserved = readUInt32(buf);    buf += 4;

    //TODO: 

    if(len == head_len)
    {
        return len;
    }

    if(m_header.length > (uint32)len)
    {
        setBody(data+head_len, len-head_len);
        return len;
    }
    setBody(data+head_len, m_header.length-head_len);

    return (int)(m_header.length);
}

/**byte-stream/memory-stream [big endian]
 * 高位存储在内存的低地址,低位存储在内存的高地址
 */
int16 NetBinaryPdu::readInt16( const char* buf )
{
    int16 data = (((byte)buf[0]) << 8) | ((byte)buf[1]);
    return data;
}

uint16 NetBinaryPdu::readUInt16( const char* buf )
{
    uint16 data = (((byte)buf[0]) << 8) | ((byte)buf[1]);
    return data;
}

int32 NetBinaryPdu::readInt32( const char* buf )
{
    int32 data = (((byte)buf[0]) << 24) | (((byte)buf[1]) << 16) | (((byte)buf[2]) << 8) | ((byte)buf[3]);
    return data;
}

uint32 NetBinaryPdu::readUInt32( const char* buf )
{
    uint32 data = (((byte)buf[0]) << 24) | (((byte)buf[1]) << 16) | (((byte)buf[2]) << 8) | ((byte)buf[3]);
    return data;
}

void NetBinaryPdu::writeInt16( char* buf, int16 data )
{
    buf[0] = data >> 8;
    buf[1] = data & 0xFF;
}

void NetBinaryPdu::writeUInt16( char* buf, uint16 data )
{
    buf[0] = data >> 8;
    buf[1] = data & 0xFF;
}

void NetBinaryPdu::writeInt32( char* buf, int32 data )
{
    buf[0] = data >> 24;
    buf[1] = (data >> 16) & 0xFF;
    buf[2] = (data >> 8) & 0xFF;
    buf[3] = data & 0xFF;
}

void NetBinaryPdu::writeUInt32( char* buf, uint32 data )
{
    buf[0] = data >> 24;
    buf[1] = (data >> 16) & 0xFF;
    buf[2] = (data >> 8) & 0xFF;
    buf[3] = data & 0xFF;
}




NetBinaryPduRequest::NetBinaryPduRequest()
{
    m_header.flag = NET_PDU_REQUEST; //request
}

NetBinaryPduRequest::~NetBinaryPduRequest() { }

smart::ref_ptr<NetBinaryPduResponse> NetBinaryPduRequest::createResponse()
{
    NetBinaryPduResponse* response = new NetBinaryPduResponse();
    response->setOperation(m_header.operation);
    response->setSeq(m_header.seq);


    return response;
}

const char* NetBinaryPduRequest::toStream()
{
    return NetBinaryPdu::toStream();
}

int NetBinaryPduRequest::fromStream( const char* data, int len )
{
    return NetBinaryPdu::fromStream(data, len);
}




NetBinaryPduResponse::NetBinaryPduResponse()
{
    m_header.flag = NET_PDU_RESPONSE; //response
}

NetBinaryPduResponse::~NetBinaryPduResponse() { }

const char* NetBinaryPduResponse::toStream()
{
    return NetBinaryPdu::toStream();
}

int NetBinaryPduResponse::fromStream( const char* data, int len )
{
    return NetBinaryPdu::fromStream(data, len);
}
